//
//  RecogizeCardManager.m
//  RecognizeCard
//
//  Created by 谭高丰 on 16/8/31.
//  Copyright © 2016年 谭高丰. All rights reserved.
//

#import "RecogizeCardManager.h"
#import <opencv2/opencv.hpp>
#import <opencv2/imgproc/types_c.h>
#import <opencv2/imgcodecs/ios.h>
//#import <TesseractOCR/TesseractOCR.h>

@implementation RecogizeCardManager

+ (instancetype)recognizeCardManager {
    static RecogizeCardManager *recognizeCardManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        recognizeCardManager = [[RecogizeCardManager alloc] init];
    });
    return recognizeCardManager;
}

//- (void)recognizeCardWithImage:(UIImage *)cardImage compleate:(CompleateBlock)compleate {
//    //扫描身份证图片，并进行预处理，定位号码区域图片并返回
//    UIImage *numberImage = [self opencvScanCard:cardImage];
//    if (numberImage == nil) {
//        compleate(nil);
//    }
//    //利用TesseractOCR识别文字
//    [self tesseractRecognizeImage:numberImage compleate:^(NSString *numbaerText) {
//        compleate(numbaerText);
//    }];
//}

- (void)testRecognizeCardWithImage:(UIImage *)cardImage compleate:(CompleateDic)compleate
{
    //扫描身份证图片，并进行预处理，定位号码区域图片并返回
    NSDictionary *dic = [self testOpencvRange:cardImage];
    compleate(dic);
}


// 检测轮廓
- (NSDictionary *)testOpencvRange:(UIImage *)image{
    
    //将UIImage转换成Mat
    cv::Mat resultImage;
    UIImageToMat(image, resultImage);
    
    //转为灰度图
    cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
    //利用阈值二值化
    cv::threshold(resultImage, resultImage, 120, 255, CV_THRESH_BINARY);
//    cv::threshold(resultImage, resultImage, 150, 255, CV_THRESH_BINARY_INV);
    
    
    //
    int maxVal = 255;
    int blockSize = 55;
    double C = 0;
    
    // 自适应阀值，平均阀值
//    cv::adaptiveThreshold(resultImage, resultImage, maxVal, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY_INV, blockSize, C);
    // 自适应阀值，高斯阀值
    cv::adaptiveThreshold(resultImage, resultImage, maxVal, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, blockSize, C);
    
    
//    腐蚀，填充（腐蚀是让黑色点变大）
//    cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(2,2));
//    cv::erode(resultImage, resultImage, erodeElement);
//    cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(25,1));
//    cv::erode(resultImage, resultImage, erodeElement);
    
    // 高斯滤波
    cv::GaussianBlur(resultImage, resultImage, cv::Size(5,5), cv::BORDER_DEFAULT);
//
//  // 均值滤波
//    cv::blur(resultImage, resultImage, cv::Size(5,5));
    
    // 中值滤波
//    cv::medianBlur(resultImage, resultImage, 5);
    
    // Canny算子
//    int thr1 = 60;
//    int thr2 = thr1 * 3;
//    cv::Canny(resultImage, resultImage, thr1, thr2);
    
    // Sobel 算子
//    cv::Sobel(resultImage, resultImage, CV_32SC1, 1, 0);
    
    
    UIImage *dealImage = MatToUIImage(resultImage);
    
    
    //轮廊检测
    std::vector<std::vector<cv::Point>> contours;//定义一个容器来存储所有检测到的轮廊
    cv::findContours(resultImage, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
    cv::drawContours(resultImage, contours, -1, cv::Scalar(255),4);
    //取出身份证号码区域
    std::vector<cv::Rect> rects;
    cv::Rect numberRect = cv::Rect(0,0,0,0);
    std::vector<std::vector<cv::Point>>::const_iterator itContours = contours.begin();
    for ( ; itContours != contours.end(); ++itContours) {
        cv::Rect rect = cv::boundingRect(*itContours);
        rects.push_back(rect);
        //算法原理
        if ((rect.x > 10) &&
            (rect.y > 10) &&
            (rect.width >= 100) &&
            (rect.height >= 50) &&
            (rect.width > rect.height) &&
            (rect.width < rect.height * 3.0) &&
            (rect.width > numberRect.width))
        {
            numberRect = rect;
            
        }
    }
    //身份证号码定位失败
    if (numberRect.width == 0 || numberRect.height == 0) {
        return nil;
    }
    //定位成功成功，去原图截取身份证号码区域，并转换成灰度图、进行二值化处理
    cv::Mat matImage;
    UIImageToMat(image, matImage);
    resultImage = matImage(numberRect);
    
    cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
//    cv::threshold(resultImage, resultImage, 50, 255, CV_THRESH_BINARY);
    //将Mat转换成UIImage
    UIImage *numberImage = MatToUIImage(resultImage);
    
    CGRect range = CGRectMake(numberRect.x, numberRect.y, numberRect.width, numberRect.height);
    
    NSDictionary * dic = @{
        @"dealAgo": dealImage, @"dealAfter": numberImage, @"range": [NSValue valueWithCGRect:range]
    };
//    NSDictionary * dic = @{
//        @"range": [NSValue valueWithCGRect:range]
//    };
    
    return dic;
}


- (UIImage *)testOpencvWithImage:(UIImage *)image{
    //将UIImage转换成Mat
    cv::Mat resultImage;
    UIImageToMat(image, resultImage);
    //转为灰度图
    cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
    //利用阈值二值化
    cv::threshold(resultImage, resultImage, 10, 255, CV_THRESH_BINARY);
    //腐蚀，填充（腐蚀是让黑色点变大）
//    cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(26,26));
//    cv::erode(resultImage, resultImage, erodeElement);
//    cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(25,1));
//    cv::erode(resultImage, resultImage, erodeElement);
    //轮廊检测
    std::vector<std::vector<cv::Point>> contours;//定义一个容器来存储所有检测到的轮廊
    cv::findContours(resultImage, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
    cv::drawContours(resultImage, contours, -1, cv::Scalar(255),4);
    //取出身份证号码区域
    std::vector<cv::Rect> rects;
    cv::Rect numberRect = cv::Rect(0,0,0,0);
    std::vector<std::vector<cv::Point>>::const_iterator itContours = contours.begin();
    for ( ; itContours != contours.end(); ++itContours) {
        cv::Rect rect = cv::boundingRect(*itContours);
        rects.push_back(rect);
        //算法原理
        if (rect.width > numberRect.width && rect.width > rect.height * 5) {
            numberRect = rect;
        }
    }
    //身份证号码定位失败
    if (numberRect.width == 0 || numberRect.height == 0) {
        return nil;
    }
    //定位成功成功，去原图截取身份证号码区域，并转换成灰度图、进行二值化处理
    cv::Mat matImage;
    UIImageToMat(image, matImage);
    resultImage = matImage(numberRect);
    
    cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
    cv::threshold(resultImage, resultImage, 50, 255, CV_THRESH_BINARY);
    //将Mat转换成UIImage
    UIImage *numberImage = MatToUIImage(resultImage);
    return numberImage;
}



//扫描身份证图片，并进行预处理，定位号码区域图片并返回
- (UIImage *)opencvScanCard:(UIImage *)image {
    
    //将UIImage转换成Mat
    cv::Mat resultImage;
    UIImageToMat(image, resultImage);
    //转为灰度图
    cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
    //利用阈值二值化 
    cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
    //腐蚀，填充（腐蚀是让黑色点变大）
    cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(26,26));
    cv::erode(resultImage, resultImage, erodeElement);
    //轮廊检测 
    std::vector<std::vector<cv::Point>> contours;//定义一个容器来存储所有检测到的轮廊
    cv::findContours(resultImage, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
    //cv::drawContours(resultImage, contours, -1, cv::Scalar(255),4);
    //取出身份证号码区域
    std::vector<cv::Rect> rects;
    cv::Rect numberRect = cv::Rect(0,0,0,0);
    std::vector<std::vector<cv::Point>>::const_iterator itContours = contours.begin();
    for ( ; itContours != contours.end(); ++itContours) {
        cv::Rect rect = cv::boundingRect(*itContours);
        rects.push_back(rect);
        //算法原理
        if (rect.width > numberRect.width && rect.width > rect.height * 5) {
            numberRect = rect;
        }
    }    
    //身份证号码定位失败
    if (numberRect.width == 0 || numberRect.height == 0) {
        return nil;
    }
    //定位成功成功，去原图截取身份证号码区域，并转换成灰度图、进行二值化处理
    cv::Mat matImage;
    UIImageToMat(image, matImage);
    resultImage = matImage(numberRect);
    cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
    cv::threshold(resultImage, resultImage, 80, 255, CV_THRESH_BINARY);
    //将Mat转换成UIImage
    UIImage *numberImage = MatToUIImage(resultImage);
    return numberImage;
}

//利用TesseractOCR识别文字
//- (void)tesseractRecognizeImage:(UIImage *)image compleate:(CompleateBlock)compleate {
//
//    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
//        G8Tesseract *tesseract = [[G8Tesseract alloc] initWithLanguage:@"eng"];
//        tesseract.image = [image g8_blackAndWhite];
//        tesseract.image = image;
//        // Start the recognition
//        [tesseract recognize];
//        //执行回调
//        compleate(tesseract.recognizedText);
//    });
//}


- (cv::Mat)cvMatFromUIImage:(UIImage *)image
{
  CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
  CGFloat cols = image.size.width;
  CGFloat rows = image.size.height;

  cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels (color channels + alpha)

  CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to  data
                                                 cols,                       // Width of bitmap
                                                 rows,                       // Height of bitmap
                                                 8,                          // Bits per component
                                                 cvMat.step[0],              // Bytes per row
                                                 colorSpace,                 // Colorspace
                                                 kCGImageAlphaNoneSkipLast |
                                                 kCGBitmapByteOrderDefault); // Bitmap info flags

  CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
  CGContextRelease(contextRef);

  return cvMat;
}

- (cv::Mat)cvMatGrayFromUIImage:(UIImage *)image
{
  CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
  CGFloat cols = image.size.width;
  CGFloat rows = image.size.height;

  cv::Mat cvMat(rows, cols, CV_8UC1); // 8 bits per component, 1 channels

  CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to data
                                                 cols,                       // Width of bitmap
                                                 rows,                       // Height of bitmap
                                                 8,                          // Bits per component
                                                 cvMat.step[0],              // Bytes per row
                                                 colorSpace,                 // Colorspace
                                                 kCGImageAlphaNoneSkipLast |
                                                 kCGBitmapByteOrderDefault); // Bitmap info flags

  CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
  CGContextRelease(contextRef);

  return cvMat;
 }
@end
